/*
 * Types.c
 *
 *  Created on: 24/apr/2013
 *      Authors: Ialenti Andrea, Marangon Fabio
 */

#include "../headers/Types.h"
#include "../headers/Utility.h"

#include <stdlib.h>
#include <string.h>

bool initGraph(const Config *cfg, Graph *g, int nodes, int seed) {
	char *funcName = initCharArray(cfg->NAME_LEN);
	sprintf(funcName, "initGraph");

	int i;

	g->size = nodes;
	g->edgeCnt = g->size * (g->size - 1) / 2;
	g->seed = seed;
	g->min = 0.0;
	g->max = 1.0;

	g->nodes = (Node*) calloc(g->size, sizeof(Node));
	g->edges = (Edge**) calloc(g->size, sizeof(Edge*));
	g->u = (double*) calloc(g->size, sizeof(double));
	g->name = (char*) calloc(cfg->NAME_LEN, sizeof(char));
	g->costs_origin = (double**) calloc(g->size, sizeof(double*));
	if (g->nodes == NULL || g->edges == NULL || g->u == NULL || g->name == NULL || g->costs_origin == NULL) {
		fprintf(stdout, "***'%s': memory error during 'Graph' data structure initialization!\n", funcName);
		freeCharArray(funcName);
		return false;
	}

	for (i = 0; i < g->size; i++) {
		g->edges[i] = (Edge*) calloc(g->size, sizeof(Edge));
		g->costs_origin[i] = (double*) calloc(g->size, sizeof(double));
		if (g->edges[i] == NULL || g->costs_origin[i] == NULL) {
			fprintf(stdout, "***'%s': memory error during 'Graph' data structure initialization!\n", funcName);
			freeCharArray(funcName);
			return false;
		}
	}

	freeCharArray(funcName);
	return true;
}

bool initOneTree(const Config *cfg, OneTree *t, int nodes) {
	char *funcName = initCharArray(cfg->NAME_LEN);
	sprintf(funcName, "initOneTree");

	int i;

	t->size = nodes;
	t->v0 = 0;
	t->totalCost = 0.0;
	t->tc = 0.0;

	t->nodes = (Node*) calloc(t->size, sizeof(Node));
	t->edges = (Edge*) calloc(t->size, sizeof(Edge));
	t->name = (char*) calloc(cfg->NAME_LEN, sizeof(char));
	t->costs = (double**) calloc(t->size, sizeof(double*));
	if (t->nodes == NULL || t->edges == NULL || t->name == NULL	|| t->costs == NULL) {
		fprintf(stdout, "***'%s': memory error during 'OneTree' data structure initialization!\n", funcName);
		freeCharArray(funcName);
		return false;
	}

	for (i = 0; i < t->size; i++) {
		t->costs[i] = (double*) calloc(t->size, sizeof(double));
		if (t->costs[i] == NULL) {
			fprintf(stdout, "***'%s': memory error during 'OneTree' data structure initialization!\n", funcName);
			freeCharArray(funcName);
			return false;
		}
	}
	freeCharArray(funcName);
	return true;
}

bool initFuncLu(const Config *cfg, funcLu *f, int itN, int step) {
	char *funcName = initCharArray(cfg->NAME_LEN);
	sprintf(funcName, "initFuncLu");

	f->size = itN;
	f->step = step;
	f->best = 0;
	f->kBest = 0;
	f->upperBound = 0;
	f->L = (double*) calloc(f->size, sizeof(double));
	f->name = (char*) calloc(cfg->NAME_LEN, sizeof(char));
	if (f->L == NULL || f->name == NULL) {
		fprintf(stdout, "***'%s': memory error during 'funcLu' data structure initialization!\n", funcName);
		freeCharArray(funcName);
		return false;
	}
	freeCharArray(funcName);
	return true;
}

bool initProblem(const Config *cfg, Problem *p, int nodes, int actualEdges) {
	char *funcName = initCharArray(cfg->NAME_LEN);
	sprintf(funcName, "initProblem");

	int i;

	//	Initialize problem size.
	p->numrows = p->nodes = nodes;
	p->numcols = p->edges = actualEdges;
	p->nonZero = 0;

	//	Initialize strings.
	p->name = (char*) calloc(cfg->NAME_LEN, sizeof(char));
	p->varNames = (char**) calloc(p->numcols, sizeof(char*));
	p->constrNames = (char**) calloc(p->numrows, sizeof(char*));
	p->constrIdx = (int*) calloc(p->numrows, sizeof(int));

	//	Initialize correspondences variables.
	p->Xe = (int*) calloc(p->numcols, sizeof(int));
	p->XeCost = (double*) calloc(p->numcols, sizeof(double));
	p->pos = (int**) calloc(p->numcols, sizeof(int*));
	p->posReverse = (int**) calloc(p->numcols, sizeof(int*));
	if (p->name == NULL || p->varNames == NULL || p->constrNames == NULL || p->constrIdx == NULL || p->Xe == NULL || p->XeCost == NULL || p->pos == NULL || p->posReverse == NULL) {
		fprintf(stdout, "***'%s': memory error during 'Problem' data structure initialization!\n", funcName);
		freeCharArray(funcName);
		return false;
	}
	for (i = 0; i < p->numcols; i++) {
		p->posReverse[i] = (int*) calloc(2, sizeof(int));
		p->pos[i] = (int*) calloc(2, sizeof(int));
		p->varNames[i] = (char*) calloc(cfg->CSTR_NAME_LEN, sizeof(char));
		if (p->pos[i] == NULL || p->posReverse[i] == NULL
				|| p->varNames[i] == NULL) {
			fprintf(stdout, "***'%s': memory error during 'Problem' data structure initialization!\n", funcName);
			freeCharArray(funcName);
			return false;
		}
	}
	for (i = 0; i < p->numrows; i++) {
		p->constrNames[i] = (char*) calloc(cfg->CSTR_NAME_LEN, sizeof(char));
		if (p->constrNames[i] == NULL) {
			fprintf(stdout, "***'%s': memory error during 'Problem' data structure initialization!\n", funcName);
			freeCharArray(funcName);
			return false;
		}
	}

	//	Initialize position matrix.
	for (i = 0; i < p->numcols; i++)
		p->posReverse[i][0] = p->posReverse[i][1] = -1;

	//	Initialize CPLEX-related arrays.
	p->probname = (char*) calloc(cfg->NAME_LEN, sizeof(char));
	p->objCoeff = (double*) calloc(p->numcols, sizeof(double));
	p->rhs = (double*) calloc(p->numrows, sizeof(double));
	p->sense = (char*) calloc(p->numrows, sizeof(char));
	p->matbeg = (int*) calloc(p->numcols, sizeof(int));
	p->matcnt = (int*) calloc(p->numcols, sizeof(int));
	p->lb = (double*) calloc(p->numcols, sizeof(double));
	p->ub = (double*) calloc(p->numcols, sizeof(double));
	p->ctype = (char*) calloc(p->numcols, sizeof(char));

	if (p->probname == NULL || p->objCoeff == NULL || p->rhs == NULL || p->sense == NULL || p->matbeg == NULL || p->matcnt == NULL || p->lb == NULL || p->ub == NULL || p->ctype == NULL) {
		fprintf(stdout, "***'%s': memory error during 'Problem' data structure initialization!\n", funcName);
		freeCharArray(funcName);
		return false;
	}

	//Initialize CPLEX-related proximity search variables.
	p->heuSol = 0;
	p->beg[0] = 0;
	p->vals = (double*) calloc(p->numcols, sizeof(double));
	p->idx = (int*) calloc(p->numcols, sizeof(int));
	p->psName = (char**) calloc(1, sizeof(char*));

	if (p->vals == NULL || p->idx == NULL || p->psName == NULL) {
		fprintf(stdout, "***'%s': memory error during 'Problem' data structure initialization!\n", funcName);
		freeCharArray(funcName);
		return false;
	}

	p->psName[0] = (char*) calloc(cfg->CSTR_NAME_LEN, sizeof(char));
	if (p->psName[0] == NULL) {
		fprintf(stdout, "***'%s': memory error during 'Problem' data structure initialization!\n", funcName);
		freeCharArray(funcName);
		return false;
	}

	freeCharArray(funcName);
	return true;
}

bool initSEC(const Config *cfg, SEC *s, int nodes) {
	char *funcName = initCharArray(cfg->NAME_LEN);
	sprintf(funcName, "initSEC");

	//	Initialize single variables.
	s->size = nodes;
	s->nonZero = 0;
	s->sense[1] = 'L';
	s->rhs[1] = 0;

	//	Initialize memory for the arrays.
	s->constrName[0] = (char*) calloc(cfg->NAME_LEN, sizeof(char));
	s->matbeg = (int*) calloc(s->size, sizeof(int));
	s->matind = (int*) calloc(s->size, sizeof(int));
	s->matval = (double*) calloc(s->size, sizeof(double));
	if (s->constrName == NULL || s->matbeg == NULL || s->matind == NULL || s->matval == NULL) {
		fprintf(stdout, "***'%s': memory error during 'SEC' data structure initialization!\n", funcName);
		freeCharArray(funcName);
		return false;
	}
	freeCharArray(funcName);
	return true;
}

bool initSECsArray(const Config *cfg, SEC **s, int size, int nodes) {
	char *funcName = initCharArray(cfg->NAME_LEN);
	sprintf(funcName, "initSECsArray");

	int i;
	*s = (SEC*) calloc(size, sizeof(SEC));
	if (*s == NULL) {
		fprintf(stdout, "***'%s': SECs memory allocation error!\n",funcName);
		freeCharArray(funcName);
		return false;
	}
	for (i = 0; i < size; i++)
		if (!initSEC(cfg, &(*s)[i], nodes)) {
			fprintf(stdout, "***'%s': SECs memory allocation error!\n", funcName);
			freeCharArray(funcName);
			return false;
		}

	freeCharArray(funcName);
	return true;
}

bool initCBData(const Config *cfg, cbData *data, Graph *g, Problem *p, CPXLPptr *lp, bool verbose) {
	data->lp = *lp;
	data->p = p;
	data->g = g;
	data->numcols = p->numcols;
	data->totSECs = 0;
	data->solCnt = 0;
	data->x = NULL;
	data->totalCost = 0;
	data->cntLSSol = 0;
	data->bestLSSol = cfg->INF;
	data->cntLS = 0;
	data->cntMC = 0;
	data->cntMCTot = 0;
	data->verbose = verbose;
	data->cfg = cfg;

	return true;
}

Status initCfgData(Config *cfg, const char *fileName) {
	char *funcName = initCharArray(15);
	sprintf(funcName, "initCfgData");

	FILE *file;
	char *line = initCharArray(100);
	char *param;
	char *value;

	//	Open the file in read text mode.
	if ((file = fopen (fileName, "rt")) == NULL) {
		fprintf(stdout, "***'%s': cannot read configuration file '%s'!\n", funcName, fileName);
		freeCharArray(funcName);
		freeCharArray(line);
		return ERR_READ_FILE;
	}

	//	Read file, line by line.
	while (fgets (line, 100, file) != NULL && strcmp(line, "EOF\n") != 0) {

		//	Skip blank lines and comments.
		if (strcmp(line, "\n") == 0)
			continue;
		if (strstr(line, "//") != NULL)
			continue;

		//	Tokenize each line and remove the '\n' terminating each 'value' string.
		param = strtok(line, " : ");
		value = strtok(NULL, " : ");
		strtok(value, "\n");

		//	Store the configuration parameters.
		if (strcmp(param, "MAX_LINE_LEN") == 0)
			cfg->MAX_LINE_LEN = atoi(value);
		else if (strcmp(param, "PI") == 0)
			cfg->PI = atof(value);
		else if (strcmp(param, "RADIUS") == 0)
			cfg->RADIUS = atof(value);
		else if (strcmp(param, "DFLT_VERBOSITY") == 0)
			cfg->DFLT_VERBOSITY = (strcmp(value, "true") == 0) ? true : false;
		else if (strcmp(param, "DFLT_PROB") == 0) {
			cfg->DFLT_PROB = initCharArray(cfg->NAME_LEN);
			strcpy(cfg->DFLT_PROB, value);
		}
		else if (strcmp(param, "DFLT_USE_PS") == 0)
			cfg->DFLT_USE_PS = (strcmp(value, "true") == 0) ? true : false;
		else if (strcmp(param, "DFLT_USE_CPLEX") == 0)
			cfg->DFLT_USE_CPLEX = (strcmp(value, "true") == 0) ? true : false;
		else if (strcmp(param, "DFLT_USE_CB") == 0)
			cfg->DFLT_USE_CB = (strcmp(value, "true") == 0) ? true : false;
		else if (strcmp(param, "DFLT_NODE_PERM") == 0)
			cfg->DFLT_NODE_PERM = (strcmp(value, "true") == 0) ? true : false;
		else if (strcmp(param, "DFLT_USE_3OPT") == 0)
			cfg->DFLT_USE_3OPT = (strcmp(value, "true") == 0) ? true : false;
		else if (strcmp(param, "UB_INC") == 0)
			cfg->UB_INC = atof(value);
		else if (strcmp(param, "RAND_STEP") == 0)
			cfg->RAND_STEP = atoi(value);
		else if (strcmp(param, "MAX_IT") == 0)
			cfg->MAX_IT = atoi(value);
		else if (strcmp(param, "STEP") == 0)
			cfg->STEP = atoi(value);
		else if (strcmp(param, "IT_3OPT") == 0)
			cfg->IT_3OPT = atoi(value);
		else if (strcmp(param, "SIZE_H") == 0)
			cfg->SIZE_H = atoi(value);
		else if (strcmp(param, "SIZE_W") == 0)
			cfg->SIZE_W = atoi(value);
		else if (strcmp(param, "COLOR_BLUE") == 0) {
			cfg->COLOR_BLUE = initCharArray(cfg->NAME_LEN);
			strcpy(cfg->COLOR_BLUE, value);
		}
		else if (strcmp(param, "COLOR_BLACK") == 0) {
			cfg->COLOR_BLACK = initCharArray(cfg->NAME_LEN);
			strcpy(cfg->COLOR_BLACK, value);
		}
		else if (strcmp(param, "COLOR_RED") == 0) {
			cfg->COLOR_RED = initCharArray(cfg->NAME_LEN);
			strcpy(cfg->COLOR_RED, value);
		}
		else if (strcmp(param, "COLOR_GREEN") == 0) {
			cfg->COLOR_GREEN = initCharArray(cfg->NAME_LEN);
			strcpy(cfg->COLOR_GREEN, value);
		}
		else if (strcmp(param, "PLT_PATH") == 0) {
			cfg->PLT_PATH = initCharArray(cfg->NAME_LEN);
			strcpy(cfg->PLT_PATH, value);
		}
		else if (strcmp(param, "INF") == 0)
			cfg->INF = atol(value);
		else if (strcmp(param, "EPS") == 0)
			cfg->EPS = atof(value);
		else if (strcmp(param, "NAME_LEN") == 0)
			cfg->NAME_LEN = atoi(value);
		else if (strcmp(param, "CSTR_NAME_LEN") == 0)
			cfg->CSTR_NAME_LEN = atoi(value);
		else if (strcmp(param, "CPX_TH_CNT") == 0) {
			int val = atoi(value);
			if (val <= 1)
				val = 1;
			cfg->CPX_TH_CNT = val;
		}
		else if (strcmp(param, "CPX_CB_PLT") == 0)
			cfg->CPX_CB_PLT = atoi(value);
		else if (strcmp(param, "CPX_PS_C_NAME") == 0) {
			cfg->CPX_PS_C_NAME = initCharArray(cfg->NAME_LEN);
			strcpy(cfg->CPX_PS_C_NAME, value);
		}
		else if (strcmp(param, "CPX_FILE_PATH") == 0) {
			cfg->CPX_FILE_PATH = initCharArray(cfg->NAME_LEN);
			strcpy(cfg->CPX_FILE_PATH, value);
		}
		else if (strcmp(param, "CPX_PS_TL") == 0)
			cfg->CPX_PS_TL = atof(value);
		else if (strcmp(param, "CPX_PS_ML") == 0)
			cfg->CPX_PS_ML = atof(value);
		else if (strcmp(param, "CPX_PS_GTL") == 0)
			cfg->CPX_PS_GTL = atoi(value);
		else if (strcmp(param, "CPX_TIME_LIM") == 0) {
			double val = atof(value);
			if (val < 0)
				val = 1e+75;
			cfg->CPX_TIME_LIM = val;
		}
		else if (strcmp(param, "CPX_MEM_LIM") == 0) {
			double val = atof(value);
			if (val < 0)
				val = 1e+75;
			cfg->CPX_MEM_LIM = val;
		}
		else if (strcmp(param, "NORM_TIME") == 0)
			cfg->NORM_TIME = atof(value);
		else if (strcmp(param, "B_B_OUTPUT") == 0)
			cfg->B_B_OUTPUT = atoi(value);
		else if (strcmp(param, "B_B_TIME_LIM") == 0)
			cfg->B_B_TIME_LIMT = atoi(value);
	}

	cfg->TERM_X = "x11 persist";
	cfg->plt = gnuplot_init();

	freeCharArray(funcName);
	freeCharArray(line);
	return SUCCESS;
}

void disposeGraph(Graph *g) {
	int i;

	//	Free the nodes array.
	free(g->nodes);

	//	Free the lagrangian coefficients array.
	free(g->u);

	//	Free the edges matrix.
	for (i = 0; i < g->size; i++) {
		free(g->edges[i]);
		g->edges[i] = NULL;
	}
	free(g->edges);

	//	Free the original cost matrix.
	for (i = 0; i < g->size; i++) {
		free(g->costs_origin[i]);
		g->costs_origin[i] = NULL;
	}
	free(g->costs_origin);

	//	Free the name array.
	free(g->name);

	g->nodes = NULL;
	g->u = NULL;
	g->edges = NULL;
	g->costs_origin = NULL;
	g->name = NULL;
	return;
}

void disposeOneTree(OneTree *t) {
	int i;

	//	Free the nodes and edges arrays.
	free(t->edges);
	free(t->nodes);

	//	Free the cost matrix.
	for (i = 0; i < t->size; i++) {
		free(t->costs[i]);
		t->costs[i] = NULL;
	}
	free(t->costs);

	//	Free the name array.
	free(t->name);

	t->edges = NULL;
	t->nodes = NULL;
	t->costs = NULL;
	t->name = NULL;
	return;
}

void disposeFuncLu(funcLu *f) {

	//	Free the function array.
	free(f->L);

	//	Free the name array.
	free(f->name);

	f->L = NULL;
	f->name = NULL;
	return;
}

void disposeProblem(Problem *p) {
	int i;

	//	Free the arrays.
	free(p->Xe);
	free(p->XeCost);
	free(p->probname);
	free(p->objCoeff);
	free(p->rhs);
	free(p->sense);
	free(p->matbeg);
	free(p->matcnt);
	free(p->matind);
	free(p->matval);
	free(p->lb);
	free(p->ub);
	free(p->ctype);

	//	Free the position matrix.
	for (i = 0; i < p->numrows; i++) {
		free(p->pos[i]);
		p->pos[i] = NULL;
	}
	free(p->pos);

	//	Free the name array.
	free(p->name);

	p->Xe = NULL;
	p->XeCost = NULL;
	p->probname = NULL;
	p->objCoeff = NULL;
	p->rhs = NULL;
	p->sense = NULL;
	p->matbeg = NULL;
	p->matcnt = NULL;
	p->matind = NULL;
	p->matval = NULL;
	p->lb = NULL;
	p->ub = NULL;
	p->ctype = NULL;
	p->pos = NULL;
	p->name = NULL;
	return;
}

void disposeSEC(SEC *s) {

	//	Free the arrays.
	free(s->constrName[0]);
	free(s->matbeg);
	free(s->matind);
	free(s->matval);

	s->constrName[0] = NULL;
	s->matbeg = NULL;
	s->matind = NULL;
	s->matval = NULL;
	return;
}

void disposeSECsArray(SEC *s, int size) {
	int i;

	//	Free the single SECs.
	for (i = 0; i < size; i++)
		disposeSEC(&s[i]);

	//	Free the array.
	free(s);
	s = NULL;
	return;
}

void cpNode(const Node *src, Node *dest) {

	//	Copy all struct fields.
	dest->id = src->id;
	dest->x = src->x;
	dest->y = src->y;
	dest->degree = src->degree;
	dest->forced = src->forced;
	return;
}
